package com.megvii.corelib.net;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * Created by linyue on 15/12/21.
 */
public class FunctionIndicator
{
    /**
     * key - function param class, value - processor classes
     */
    private HashMap<Class<?>, List<Object>> objectMap;

    /**
     * key - processor class, value - function param classes
     */
    private HashMap<Object, FunctionMap> callbackMap;

    public FunctionIndicator()
    {
        super();
        this.objectMap = new HashMap<>();
        this.callbackMap = new HashMap<>();
    }

    //TODO 做cache，重复的类不要再做第二次搜索
    public void register(Object object)
    {
        if (object == null)
        {
            return;
        }
        if (callbackMap.containsKey(object))
        {
            callbackMap.get(object).refCount++;
            return;
        }
        Method[] methods = object.getClass().getMethods();
        for (Method method : methods)
        {
            Callback callback = method.getAnnotation(Callback.class);
            if (callback != null)
            {
                Class<?> paramClass = callback.clazz();
                registerMethod(object, paramClass, method);
            }
        }
    }

    public List<IndicateResult> indicate(Class<?> paramClass)
    {
        ArrayList<IndicateResult> results = new ArrayList<>();
        List<Object> objects = objectMap.get(paramClass);
        if (objects != null)
        {
            for (Object object : objects)
            {
                FunctionMap functionMap = callbackMap.get(object);
                Method method = functionMap.map.get(paramClass);
                results.add(new IndicateResult(object, method));
            }
        }
        return results;
    }

    public void unregister(Object object)
    {
        if (object == null)
        {
            return;
        }
        FunctionMap functionMap = callbackMap.get(object);
        if (functionMap == null)
        {
            return;
        }
        functionMap.refCount--;
        if (functionMap.refCount < 1)
        {
            callbackMap.remove(object);
            Iterator<Class<?>> iterator = functionMap.map.keySet().iterator();
            while (iterator.hasNext())
            {
                Class<?> paramClass = iterator.next();
                List<Object> list = objectMap.get(paramClass);
                list.remove(object);
                if (list.isEmpty())
                {
                    objectMap.remove(paramClass);
                }
            }
        }
    }

    private void registerMethod(Object object, Class<?> paramClass, Method method)
    {
        List<Object> objects = objectMap.get(paramClass);
        if (objects == null)
        {
            objects = new ArrayList<>();
            objectMap.put(paramClass, objects);
        }
        objects.add(object);
        FunctionMap functionMap = callbackMap.get(object);
        if (functionMap == null)
        {
            functionMap = new FunctionMap();
            callbackMap.put(object, functionMap);
        }
        functionMap.map.put(paramClass, method);
    }

    class FunctionMap
    {
        public HashMap<Class<?>, Method> map = new HashMap<>();

        //TODO 以后找个合适的地方做引用计数。。。
        public int refCount = 1;
    }

    public class IndicateResult
    {
        public Object callee;

        public Method method;

        public IndicateResult(Object callee, Method method)
        {
            super();
            this.callee = callee;
            this.method = method;
        }
    }
}
